package org.apache.servicecomb.samples.practise.houserush.gateway.handler;


import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.http.HttpStatus;
import org.apache.servicecomb.common.rest.filter.HttpClientFilter;
import org.apache.servicecomb.core.Handler;

import org.apache.servicecomb.core.Invocation;
import org.apache.servicecomb.core.definition.MicroserviceMeta;
import org.apache.servicecomb.provider.pojo.Invoker;
import org.apache.servicecomb.samples.practise.houserush.gateway.config.LoginUrlConfig;
import org.apache.servicecomb.samples.practise.houserush.gateway.rpc.UserApi;
import org.apache.servicecomb.samples.practise.houserush.gateway.rpc.po.User;
import org.apache.servicecomb.swagger.invocation.AsyncResponse;
import org.apache.servicecomb.swagger.invocation.InvocationType;
import org.apache.servicecomb.swagger.invocation.exception.InvocationException;

import java.util.concurrent.CompletableFuture;


/**
 * @Author: Snake Man
 * @system:
 * @DateTime: 2022/1/9 19:38
 * @Description:
 */
public class AuthHandler implements Handler {

    //声明login微服务的接口
    private static UserApi userApi;

    //获取管理是否鉴权接口白名单的配置类
    private static LoginUrlConfig loginUrlConfig = new LoginUrlConfig();

    //加载login微服务接口
    static {
        //这种方式是透明RPC开发模式中的consumer的底层api
        //这种方式与之前的@ReferenceRpc注解是等效的，这种方式不需要以来spring的Bean的方式
        userApi = Invoker.createProxy("login", "userApiRest", UserApi.class);
    }

    @Override
    public void init(MicroserviceMeta microserviceMeta, InvocationType invocationType) {

    }

    @Override
    public void handle(Invocation invocation, AsyncResponse asyncResponse) throws Exception {
        //如果不需要鉴权，执行原有的逻辑
        //判断请求为空就不需要鉴权
        if (invocation.getRequestEx() == null) {
            invocation.next(asyncResponse);
            return;
        }

        //获取token
        String token = invocation.getContext("Authorization");

        //获取请求接口路径 GET  login/users/{userId}
        //获取请求方法 GET
        String method = invocation.getOperationMeta().getHttpMethod();
        //获取微服务名字 login
        String microserviceName = invocation.getMicroserviceName();
        //获取具体路径 /users/{userId}
        String path = invocation.getOperationMeta().getOperationPath();
        //拼接成完整的路径
        String requestkey = method + " /" + microserviceName + path;

        //进行对比，根据提供的白名单，进行鉴权或者登录或者无效接口的处理
        if (loginUrlConfig.getNeedLoginUrlsSet().contains(requestkey)) {
            //判断token是否为空
            if (token == null) {
                //抛需要进行身份认证异常
                asyncResponse.consumerFail(new InvocationException(HttpStatus.SC_FORBIDDEN, "forbiddem", "需要身份认证"));
                return;
            }

            //如果有token，不为空，可以进行鉴权，返回用户信息
            //User user = userApi.verifyToken(token);
            CompletableFuture<User> res = userApi.verifyToken(token);
            res.whenComplete((reUser,e) -> {
            //判断返回的用户信息是否为空
            if (res.isCompletedExceptionally() || reUser == null) {
                //抛鉴权失败异常
                asyncResponse.consumerFail(new InvocationException(HttpStatus.SC_FORBIDDEN, "forbiddem", "鉴权失败"));
                return;
            }

            //鉴权成功
            //返回用户id，需要设置用户id到上下文context中
            invocation.addContext("customerId", String.valueOf(reUser.getId()));
            //把新签发的token设置到上下文context中
            invocation.addContext("newAuthorization", reUser.getToken());

            //继续执行原有的逻辑
            try {
                invocation.next(asyncResponse);
            } catch (Exception ex) {
                asyncResponse.consumerFail(new InvocationException(HttpStatus.SC_FORBIDDEN, "forbiddem", "鉴权失败"));
            }
            });
        } else if (loginUrlConfig.getNoNeedLoginUrlsSet().contains(requestkey)) {
            //不进行鉴权的接口
            //判断是否登录操作
            if ("PUT /login/users/signin".equals(requestkey)) {
                //如果是登录操作，调用login认证微服务，进行用户登录
                //获取用户数据
                ObjectMapper mapper = new ObjectMapper();
                User user = mapper.readValue(invocation.getRequestEx().getBodyBytes(), User.class);
                //调用login微服务接口进行登录
                //User reUser = userApi.signin(user);
                CompletableFuture<User> res = userApi.signin(user);
                res.whenComplete((reUser,e) ->{
                if (res.isCompletedExceptionally()||requestkey == null) {
                    //如果返回的用户为空，表示登录失败，抛异常
                    asyncResponse.consumerFail(new InvocationException(HttpStatus.SC_FORBIDDEN, "forbiddem", "登录失败"));
                    return;
                }
                //如果返回的用户不为空，表示登录成功，执行success
                asyncResponse.success(reUser);

                });
            } else {
                invocation.next(asyncResponse);
            }
        } else {
            //没有定义的接口方法
            asyncResponse.consumerFail(new InvocationException(HttpStatus.SC_FORBIDDEN, "forbiddem", "接口未定义"));
        }
    }
}


